feat(gl-sdk): on-chain send preview, balance state, and fee rates#712
Open
feat(gl-sdk): on-chain send preview, balance state, and fee rates#712
Conversation
Collaborator
|
Very nice changes 🤗 Just one call site that is missing the new |
65eafff to
ab6b399
Compare
Adds a fee-preview path for on-chain sends so wallet UIs can show the exact fee and recipient amount before broadcasting, with guarantees that the broadcast tx matches the preview. Also adds a UI-ready classification of the on-chain wallet for the withdraw entry-point, and a fee-rates API that prevents below-relay submissions. * `prepare_onchain_send(destination, amount_or_all, sat_per_vbyte)` returns the UTXOs CLN would select, the fee, and what the recipient would receive. Uses `fund_psbt` with `reserve=0` so nothing is locked. Validates `sat_per_vbyte` against the node's `min_acceptable` relay floor and rejects below-floor rates with `Error::Argument` instead of letting the user discover the failure post-broadcast. * `onchain_send` gains optional `sat_per_vbyte` and `utxos` parameters. Passing the prepared `utxos` and `sat_per_vbyte` back reproduces the previewed fee bit-for-bit (CLN's `Withdraw` honors explicit inputs). * `Node::onchain_balance_state()` classifies the on-chain wallet into Unavailable / Available / ReserveOnly / PendingConfirmation / Immature for the withdraw entry-point. Runs `list_funds`, `list_peer_channels`, and a non-locking `fund_psbt(reserve=0, satoshi=All)` probe in parallel, deriving the actual emergency-reserve carving from `total_inputs - excess - fee` rather than guessing from channel presence. This is the single source of truth for the reserve; `NodeState` does not surface a separate field for it. * `Node::onchain_fee_rates()` returns `OnchainFeeRates` with five sat/vbyte buckets at confirmation targets (next_block, half_hour, hour, day, minimum_relay) sourced from CLN's `feerates` RPC. No third-party HTTP, no privacy leak. Field names carry units (`*_sat_per_vbyte`) for consistency with the other on-chain types on this branch. Implementation cross-checked against CLN source (`lightning/wallet/reservation.c`, `lightning/common/amount.c`, `lightning/bitcoin/tx.c`): * Fee math `weight × feerate_per_kw / 1000` matches CLN exactly. * `startweight` includes both the destination output and the 42-wu base tx core overhead per `bitcoin_tx_core_weight(1, 1)`. * Inputs are extracted from the returned PSBT, since CLN only emits the `reservations` array when `reserve > 0`. * `total_input_sat` is summed directly from PSBT inputs to remain correct on sweeps where CLN carves an emergency-reserve change output. Adds `bitcoin = "0.32"` (with `base64` feature) for PSBT parsing and proper address-based output weight computation. Includes 35 unit tests covering amount parsing, output-weight derivation, balance-state classification across real-wallet scenarios, and fee-rates bucket mapping.
ab6b399 to
572dd0b
Compare
Contributor
Author
Should be fixed, also added |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a fee-preview path for on-chain sends, a UI-ready classification of the on-chain wallet for the withdraw entry-point, and a fee-rates API that prevents below-relay submissions.
prepare_onchain_send(destination, amount_or_all, sat_per_vbyte)— runs CLN's coin selection at the given fee rate viafund_psbt(reserve=0)and returns the chosen UTXOs, fee, and post-fee recipient amount. No reservations, no discard step. Validatessat_per_vbyteagainst the node'smin_acceptablerelay floor and rejects below-floor rates withError::Argumentinstead of letting the user discover the failure post-broadcast.onchain_sendgains optionalsat_per_vbyteandutxosparameters. Passing the preparedutxosandsat_per_vbyteback reproduces the previewed fee bit-for-bit (CLN'sWithdrawhonors explicit inputs).Node::onchain_balance_state()classifies the on-chain wallet intoUnavailable/Available/ReserveOnly/PendingConfirmation/Immaturefor the withdraw entry-point. Runslist_funds,list_peer_channels, and a non-lockingfund_psbt(reserve=0, satoshi=All)probe in parallel, deriving the actual emergency-reserve carving fromtotal_inputs − excess − feerather than guessing from channel presence. This is the single source of truth for the reserve;NodeStatedoes not surface a separate field for it.Node::onchain_fee_rates()returnsOnchainFeeRateswith five sat/vbyte buckets at confirmation targets (next_block_sat_per_vbyte,half_hour_sat_per_vbyte,hour_sat_per_vbyte,day_sat_per_vbyte,minimum_relay_sat_per_vbyte) sourced from CLN'sfeeratesRPC — no third-party HTTP, no privacy leak. Field names carry units for consistency with the other on-chain types on this branch.Correctness vs. Core Lightning source
Cross-checked against
lightning/wallet/reservation.c,lightning/common/amount.c,lightning/bitcoin/tx.c:fee_sat = weight × feerate_per_kw / 1000matchesamount_tx_feeincommon/amount.c:704.startweightincludes both the destination output weight (computed viabitcoin::Address::from_str(...).script_pubkey().len()) and the 42-wu base tx core overhead perbitcoin_tx_core_weight(1, 1). Without this we would have undercounted the fee by ~10–1000 sats depending on rate.reservationsarray whenreserve > 0(seereservation.c:421). Readingreservationswithreserve=0would silently return an empty list and break the pin-and-resend round-trip.total_input_satis summed directly from PSBT inputs to remain correct on sweeps where CLN'schange_for_emergency(reservation.c:443) carves an emergency-reserve change output even onsatoshi=All.Dependencies
bitcoin = { version = \"0.32\", features = [\"base64\"] }. The crate is already in the workspace lockfile transitively viagl-client; thebase64feature is required forPsbt::from_str.Documentation
prepare_onchain_senddoc comment directs callers to use itsrecipient_satfor "Send Max" UIs (the only authoritative post-fee amount).OnchainBalanceState::Available.withdrawable_satis documented as the entry-point label number.Node::onchain_fee_ratesreturns confirmation-target buckets for fee picker UIs;minimum_relay_sat_per_vbyteis the relay-floor lower bound.Bindings
Python bindings regenerated (
libs/gl-sdk/glsdk/glsdk.py). Other language bindings need regeneration viatask sdk:bindings-{kotlin,swift,ruby}.Test plan
cargo build -p gl-sdk— cleancargo test -p gl-sdk --lib— 35 unit tests passparse_amount_or_all_handles_all_variants)output_weight_for_address_per_script_type)classify_onchain_balance_*cases including captured live-wallet data)cargo clippy -p gl-sdk— clean on new codetask sdk:bindings-python)gl-testing: prepare → send round-trip on partial and sweep with anchor channels, verify fee match and emergency-reserve carving